package org.smartboot.compare;

/**
 * @author qinluo
 * @date 2024-06-07 08:38:13
 * @since 1.1.0
 */
public class Path {

    /* Path types */

    /**
     * Root type.
     */
    private static final int DEFAULT = 0;

    /**
     * normal type
     */
    public static final int NORMAL = 1;

    /**
     * List index
     */
    public static final int INDEX = 2;

    /**
     * Key Map
     */
    public static final int KEY = 3;

    /**
     * Field type.
     */
    public static final int FIELD = 4;

    /**
     * Global root path.
     */
    public final static Path ROOT = createRoot("ROOT", null);

    /**
     * Path type.
     */
    private final int type;

    /**
     * Path level.
     * <pre>{@code
     *
     * a.b.c.d ==> level = 4
     * a.b.c[1][2] ==> level = 4
     *
     * }</pre>
     *
     */
    private final int level;

    /**
     * The related object of path.
     *
     * <pre>{@code
     * DEFAULT     NULL
     * NORMAL      Customization Object
     * INDEX       Index in Array/List.
     * KEY         Key value in Map
     * FIELD       java.lang.Field in Object.

     * }</pre>
     *
     */
    private final Object relatedObject;

    /**
     * The Full path.
     *
     * <pre>{@code
     * ROOT.xxxObject.xxxField[1][2]
     * }</pre>
     */
    private final String fullPath;

    /**
     * The current path
     * <pre>{@code
     * FullPath ROOT.xxxObject.xxxField[1][2]
     * Value   [2]
     * }</pre>
     *
     */
    private final String value;

    /**
     * Parent path
     */
    private final Path parent;

    /**
     * Create root path object.
     *
     * @param value    root path value.
     * @param related  related object.
     * @return         root path.
     */
    public static Path createRoot(String value, Object related) {
        return new Path(null, DEFAULT, value, related);
    }

    public Path(Path parent, int type, String value, Object relatedObject) {
        this.type = type;
        this.relatedObject = relatedObject;
        this.value = value;
        this.parent = parent;
        this.level = (parent != null) ? parent.level + 1 : 1;

        StringBuilder sb = new StringBuilder();
        sb.append(parent != null ? parent.fullPath : "");
        boolean appendDot = type != INDEX;
        if (parent != null && parent.hasText() && appendDot) {
            sb.append(".");
        }
        sb.append(value);
        this.fullPath = sb.toString();
    }

    public int getType() {
        return type;
    }

    public int getLevel() {
        return level;
    }

    public Object getRelatedObject() {
        return relatedObject;
    }

    public String getFullPath() {
        return fullPath;
    }

    public String getValue() {
        return value;
    }

    public Path getParent() {
        return parent;
    }

    /**
     * Check current path is root.
     *
     * @return root
     */
    public boolean isRoot() {
        return type == DEFAULT;
    }

    public boolean hasText() {
        return !fullPath.isEmpty();
    }

    @Override
    public String toString() {
        return fullPath;
    }
}
